package br.ufpe.cin.dsoa.handler.dependency;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import br.ufpe.cin.dsoa.broker.BrokerListener;
import br.ufpe.cin.dsoa.broker.ServiceDescription;
import br.ufpe.cin.dsoa.manager.AdaptationManager;
import br.ufpe.cin.dsoa.manager.InvocationEvent;
import br.ufpe.cin.dsoa.monitor.Monitor;

public class DependencyManager implements InvocationHandler,
		BrokerListener { 
	
	private AdaptationManager manager;
	private Monitor monitor;
	private Dependency dependency;
	private ServiceDescription serviceDescription;
	private List<String> blackList;
	private boolean valid;

	public DependencyManager(AdaptationManager manager, Monitor monitor, Dependency dependency) {
		this.manager = manager;
		this.monitor = monitor;
		this.dependency = dependency;
		this.blackList = new ArrayList<String>();
		this.manager.getBroker().getBestService(dependency.getSpecification().getName(),
				dependency.getSlos(), this, blackList);
		this.valid = false;
	}
	
	public void setValid(boolean stateDep) {
		this.valid = stateDep;
	}

	public boolean isValid() {
		return valid;
	}

	public synchronized void setSelected(ServiceDescription serviceDescription) {
		this.serviceDescription = serviceDescription;
		this.dependency.setService(serviceDescription.getService());
		this.setValid(true);
		this.manager.validate();
	}
	
	public synchronized Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		long startTime = System.nanoTime();
		Object result = null;
		Object service = this.getServiceDescription().getService();
		String serviceName = this.getServiceDescription().getServiceMetadata().getProviderId();
		Class[] parameterTypes = method.getParameterTypes();
		InvocationEvent invocation;
		try {
			if (null != service) {
				result = method.invoke(service, args);
			} else {
				throw new IllegalStateException(
						"Required service is not available!");
			}
		} catch (Exception exception) {
			invocation = new InvocationEvent(serviceName, 
					method.getName(), parameterTypes, args, 
					exception.getClass().getName(), exception.getMessage(), 
					(exception.getCause() == null) ? null : exception.getCause().getClass().getName(), 
					(exception.getCause() == null) ? null : exception.getCause().getMessage(),
					startTime,
					System.nanoTime());
			notifyInvocation(invocation);
			throw exception;
		}
		invocation = new InvocationEvent(serviceName, 
					method.getName(), parameterTypes, args, 
					method.getReturnType().getName(), result,
					startTime,
					System.nanoTime());
		notifyInvocation(invocation);
		return result;
	}


	private void notifyInvocation(InvocationEvent invocation) {
		monitor.process(invocation);
	}

	public Dependency getDependency() {
		return this.dependency;
	}

	public ServiceDescription getServiceDescription() {
		return this.serviceDescription;
	}
}
